package com.lxw.report.engine.dataModel.report;

import com.lxw.report.engine.dataModel.column.ColumnExpressionConfig;
import com.lxw.report.engine.dataModel.column.ColumnSortType;
import com.lxw.report.engine.dataModel.column.ColumnTree;
import com.lxw.report.engine.dataModel.column.ColumnTreeNode;
import com.lxw.report.engine.enums.BIColumnTypeEnum;
import com.lxw.report.engine.enums.ExpressionTypeEnum;
import com.lxw.report.engine.utils.AviatorExprUtil;
import com.lxw.report.engine.utils.CellFormatUtil;
import com.lxw.report.engine.utils.ComparatorUtil;
import org.apache.commons.lang3.StringUtils;


import java.util.*;
import java.util.stream.Collectors;

/**
 * 报表数据集类。
 */
public class ReportDataSet {
    public static final String PATH_SEPARATOR = "$";
    /**
     * 每一行的数据
     */
    protected  List<ReportDataRow> reportDataRows;
    /**
     * 全部列
     */
    protected List<ReportColumn> reportDataColumnList;
    /**
     * 左侧维度列
     */
    protected List<ReportColumn> leftDimensionList;
    /**
     * 上侧维度列
     */
    protected List<ReportColumn> topDimensionList;
    /**
     * 真实统计列
     */
    protected List<ReportColumn> realStatisticalList;
    /**
     * 计算统计列
     */
    protected List<ReportColumn> computeStatisticalList;
    /**
     * 上侧维度列+统计列树结构
     */
    protected ColumnTree topColumnTree;
    /**
     * 左侧维度列树结构
     */
    protected ColumnTree topDimensionColumnTree;
    /**
     * 左侧维度列树结构
     */
    protected ColumnTree leftDimensionColumnTree;
    /**
     * 真实统计列
     */
    protected ColumnTree statisticalColumnTree;
    /**
     * 查询结果总数
     */
    protected Long totalCount;

    /**
     * 查询页数
     */
    protected int pageNumber;
    /**
     * 查询每页条数
     */
    protected int pageSize;

    public String getPathSeparator() {
        return ReportDataSet.PATH_SEPARATOR;
    }

    /**
     * 根据左侧与上侧维度节点获得对应的rowKey的值
     * @param rowNode
     * @param columnNode
     * @return
     */
    public String getRowKey( ColumnTreeNode rowNode,  ColumnTreeNode columnNode) {
        return (rowNode.getPath() + columnNode.getPath());
    }

    /**
     * 初始化
     */
    public ReportDataSet(List<ReportDataRow> reportDataRows, List<ReportColumn> reportDataColumnList) {
        this.reportDataRows = reportDataRows;
        this.reportDataColumnList=reportDataColumnList;
        //初始化列
        this.initColumns();
        //初始化上侧与左侧的树结构
        this.initColumnTrees();
    }

    /**
     * 初始化各个列集合
     */
    private void initColumns(){
        //左侧维度列
        this.leftDimensionList = new ArrayList<ReportColumn>();
        //上侧维度列
        this.topDimensionList = new ArrayList<ReportColumn>();
        //真实统计列
        this.realStatisticalList = new ArrayList<ReportColumn>();
        //计算统计列
        this.computeStatisticalList = new ArrayList<ReportColumn>();
        for (int i = 0; i < this.reportDataColumnList.size(); i++) {
             ReportColumn column = this.reportDataColumnList.get(i);
            if (column.getBiColumnType()== BIColumnTypeEnum.LEFT_DIMENSION) {
                this.leftDimensionList.add(column);
            }

            if (column.getBiColumnType()== BIColumnTypeEnum.TOP_DIMENSION) {
                this.topDimensionList.add(column);
            }

            if (column.getBiColumnType()== BIColumnTypeEnum.REAL_STATISTICAL) {
                this.realStatisticalList.add(column);
            }

            if (column.getBiColumnType()== BIColumnTypeEnum.COMPUTE_STATISTICAL) {
                this.computeStatisticalList.add(column);
            }
        }
    }

    /**
     * 初始化树结构
     */
    private void initColumnTrees(){
        //初始化左侧
        this.leftDimensionColumnTree=this.buildColumnTreeByLevel(this.leftDimensionList,true);
        //初始化上侧
        this.topDimensionColumnTree=this.buildColumnTreeByLevel(this.topDimensionList,true);
        //初始化统计
        List<ColumnTreeNode> treeNodes = new ArrayList<>();
        List<ReportColumn> allStatisticalList = new ArrayList<>();
        List<ReportColumn> realStatisticalList = this.getRealStatisticalList();
        List<ReportColumn> computeStatisticalList = this.getComputeStatisticalList();
        allStatisticalList.addAll(realStatisticalList);
        allStatisticalList.addAll(computeStatisticalList);
        treeNodes.addAll(allStatisticalList.stream()
                .map(this::createStatColumnTreeNode)
                .collect(Collectors.toList()));
        this.statisticalColumnTree=new ColumnTree(treeNodes, 1);
        //将上侧维度列与统计列合并为一个树结构
        //生成统计列树结构,根据是显示统计列设置树的root节点与深度
        // 如果没有维度列则表头列直接设置为统计列
        if (this.getTopDimensionList().size() == 0) {
            //重置树节点 的扩展单元格树与深度,统计列默认不排序
            this.setTreeNodeSpansAndDepth(this.statisticalColumnTree.getRoots(), false,allStatisticalList);
            //重置完统计列树节点后重新生成一个头部树结构
            this.topColumnTree = new ColumnTree(this.statisticalColumnTree.getRoots(), this.statisticalColumnTree.getDepth());
            //该种情况下叶子节点就等于root节点
            this.topColumnTree.setLeafNodes(this.statisticalColumnTree.getRoots());
        }else{
            //生成维度列的树结构
            ColumnTree tempTopDimensionColumnTree = this.getTopDimensionColumnTree();
            //统计列默认为维度列的下一级
            for (ColumnTreeNode leafNode : tempTopDimensionColumnTree.getLeafNodes()) {
                leafNode.getChildren().addAll(this.statisticalColumnTree.getRoots());
            }
            //之前已经排完序了 只需要重新计算上侧维度列最外层的 rowSpan
            this.setTreeNodeSpansAndDepth(tempTopDimensionColumnTree.getRoots(),false, this.getTopDimensionList());
            int depth = tempTopDimensionColumnTree.getDepth() + this.statisticalColumnTree.getDepth();
            this.topColumnTree = new ColumnTree(tempTopDimensionColumnTree.getRoots(), depth);
        }
    }
    /**
     * 按列的先后顺序做为树的层次，构建出一棵层次树 map集合 ，
     * 其中层次数为key(从0计算),当前层次列节点集合为value
     *
     * @param columns             列集合
     * @param isInitSpansAndDepth 是否初始树的spans与深度属性
     * @return ColumnTree
     */
    private ColumnTree buildColumnTreeByLevel(List<ReportColumn> columns, boolean isInitSpansAndDepth) {
        Map<Integer, List<ColumnTreeNode>> levelNodeMap = new HashMap<>();
        //树的深度
        int depth = columns.size();
        //得到每一节点level(从0开始)对应的树节点集合
        for (int level = 0; level < depth; level++) {
            levelNodeMap.put(level, this.getTreeNodesByLevel(columns, level));
        }
        //得到最外层的所有节点并赋值父子节点关系
        List<ColumnTreeNode> leafNodes = this.getAllLeafNodes(levelNodeMap, depth);
        if (isInitSpansAndDepth) {
            this.setTreeNodeSpansAndDepth(levelNodeMap.get(0), true, columns);
        }
        ColumnTree tree = new ColumnTree(levelNodeMap.get(0), depth);
        tree.setLeafNodes(leafNodes);
        return tree;
    }

    /**
     * 按层次遍历树并设置节点的父子关系, 同时返回树节点的所有叶子节点
     *
     * @param levelNodeMap 层次树HashMap集合
     * @param depth        树的层次深度（根层次为0)
     * @return {@link List<ColumnTreeNode>}
     */
    protected List<ColumnTreeNode> getAllLeafNodes(Map<Integer, List<ColumnTreeNode>> levelNodeMap, int depth) {
        //如果树的深度为1 则最外层节点直接就是 level为0的树节点集合,且不用赋值父子关系  否则遍历赋值节点的父子关系
        List<ColumnTreeNode> leafNodes =levelNodeMap.get(depth-1);
        //如果树的深度为1 则最外层节点直接就是 level为0的树节点集合,且不用赋值父子关系  否则遍历赋值节点的父子关系
        if(depth>1){
            for (int level = 0; level < depth - 1; level++) {
                List<ColumnTreeNode> parentNodes = levelNodeMap.get(level);
                for (ColumnTreeNode parentNode : parentNodes) {
                    List<ColumnTreeNode> subNodes = levelNodeMap.get(level + 1);
                    for (ColumnTreeNode subNode : subNodes) {
                        if (subNode.getParent().getPath().equals(parentNode.getPath())) {
                            //遍历赋值父子节点关系
                            parentNode.getChildren().add(subNode);
                        }
                    }
                }
            }
        }
        return leafNodes;
    }

    //根据节点等级(从0开始)得到该等级下的所有节点并初始化节点的父级节点
    private List<ColumnTreeNode> getTreeNodesByLevel(List<ReportColumn> columns, int level) {
        List<ReportDataRow> metaDataRows = this.reportDataRows;
        Set<String> pathSet = new HashSet<>();
        List<ColumnTreeNode> depthTreeNodes = new ArrayList<>();
        for (ReportDataRow metaDataRow : metaDataRows) {
            String path = this.getLevelPath(metaDataRow, columns, level);
            if (!pathSet.contains(path)) {
                pathSet.add(path);
                depthTreeNodes.add(this.createColumnTreeNode(metaDataRow, columns, level, path));
            }
        }
        return depthTreeNodes;
    }

    protected ColumnTreeNode createColumnTreeNode(ReportDataRow metaDataRow, List<ReportColumn> columns,
                                                  int depth, String path) {
        ColumnTreeNode parentNode = null;
        if (depth > 0) {
            ReportColumn parentColumn = columns.get(depth - 1);
            String parentValue = this.getMetaCellValue(metaDataRow, parentColumn);
            String parentPath = this.getLevelPath(metaDataRow, columns, depth - 1);
            parentNode=new ColumnTreeNode();
            parentNode.setValue(parentValue);
            parentNode.setPath(parentPath);
            parentNode.setColumn(parentColumn);
        }
        ReportColumn column = columns.get(depth);
        String value = this.getMetaCellValue(metaDataRow, column);
        ColumnTreeNode treeNode = new ColumnTreeNode();
        treeNode.setValue(value);
        treeNode.setParent(parentNode);
        treeNode.setDepth(depth);
        treeNode.setPath(path);
        treeNode.setColumn(column);
        return treeNode;
    }

    protected String getLevelPath(ReportDataRow metaDataRow, List<ReportColumn> columns, int level) {
        StringBuilder pathBuilder = new StringBuilder();
        for (int i = 0; i <= level; i++) {
            ReportColumn column = columns.get(i);
            String value = this.getMetaCellValue(metaDataRow, column);
            pathBuilder.append(StringUtils.replace(value, PATH_SEPARATOR, "*"));
            pathBuilder.append(PATH_SEPARATOR);
        }
        return pathBuilder.toString();
    }

    // 获取metaDataRow一行数据的对应column列的值
    private String getMetaCellValue(ReportDataRow metaDataRow, ReportColumn column) {
        Object cellValue = metaDataRow.getCellValue(column.getSqlColumnName());
        return (cellValue == null) ? "" : cellValue.toString();
    }
    protected void setTreeNodeSpansAndDepth(List<ColumnTreeNode> roots, boolean isSort,
                                            List<ReportColumn> columns) {
        //对每一个节点排序
        if (isSort) {
            this.sortTreeNodesByLevel(roots, 0, columns);
        }
        //设置每一个几点的合并单元格数
        for (ColumnTreeNode root : roots) {
            root.setDepth(0);
            root.setSpans(this.setSpansAndDepthByRecursion(root, 0, isSort, columns));
        }
    }
    //递归设置每一个树节点的 深度与扩展单元格数
    protected int setSpansAndDepthByRecursion(ColumnTreeNode parentNode, int depth, boolean isSort,
                                              List<ReportColumn> columns) {
        if (parentNode.getChildren().size() == 0) {
            return 1;
        }
        if (isSort) {
            this.sortTreeNodesByLevel(parentNode.getChildren(), depth + 1, columns);
        }
        int spans = 0;
        for (ColumnTreeNode treeNode : parentNode.getChildren()) {
            treeNode.setDepth(depth + 1);
            treeNode.setSpans(this.setSpansAndDepthByRecursion(treeNode, depth + 1, isSort, columns));
            spans += treeNode.getSpans();
        }
        return spans;
    }

    protected void sortTreeNodesByLevel(List<ColumnTreeNode> treeNodes, int level, List<ReportColumn> columns) {
        if (level >= columns.size()) {
            return;
        }
        ReportColumn column = columns.get(level);
        BIColumnTypeEnum biColumnType = column.getBiColumnType();
         ColumnSortType sortType = column.getSortType();
        //默认排序或者统计列不进行排序
        if (sortType == ColumnSortType.DEFAULT ||
                biColumnType == BIColumnTypeEnum.REAL_STATISTICAL ||
                biColumnType == BIColumnTypeEnum.COMPUTE_STATISTICAL) {
            return;
        }
        Collections.sort(treeNodes, (o1, o2) -> {
            if (sortType == ColumnSortType.DIGIT_ASCENDING) {
                return ComparatorUtil.compareByDigitPriority(o1.getValue(), o2.getValue());
            }
            if (sortType == ColumnSortType.DIGIT_DESCENDING) {
                return ComparatorUtil.compareByDigitPriority(o2.getValue(), o1.getValue());
            }
            if (sortType == ColumnSortType.CHAR_ASCENDING) {
                return ComparatorUtil.compareByChar(o1.getValue(),o2.getValue());
            }
            if (sortType == ColumnSortType.CHAR_DESCENDING) {
                return ComparatorUtil.compareByChar(o2.getValue(),o1.getValue());
            }
            return 0;
        });
    }

    protected ColumnTreeNode createStatColumnTreeNode(ReportColumn column) {
        ColumnTreeNode treeNode = new ColumnTreeNode();
        treeNode.setColumn(column);
        treeNode.setPath(column.getReportColumnName() + PATH_SEPARATOR);
        return treeNode;
    }

    public Map<String, ReportDataRow> getDimensionKeyAndRowMap() {
        Map<String, ReportDataRow> dimensionKeyAndRowMap = new HashMap<>();
        //计算列
        List<ReportColumn> computedColumns = this.getComputeStatisticalList();
        //统计+计算列
        List<ReportColumn> statColumns = new ArrayList<>();
        statColumns.addAll(this.getRealStatisticalList());
        statColumns.addAll(computedColumns);

        //维度列
        List<ReportColumn> dimensionColumnList = new ArrayList<>();
        dimensionColumnList.addAll(this.getLeftDimensionList());
        dimensionColumnList.addAll(this.getTopDimensionList());

        //经过分页行号处理之后的数据
        List<ReportDataRow> reportDataRows = this.reportDataRows;
        //分页后的数据 根据布局列进行分类
        Map<String,List<ReportDataRow>> metaDataRowsByLayOutKeyMap = new HashMap<>();
        for (int i = 0; i < reportDataRows.size(); i++) {
            ReportDataRow metaDataRow = reportDataRows.get(i);
            //为占比功能记录数据
            if(metaDataRowsByLayOutKeyMap.containsKey(metaDataRow.getLayOutRowKey())){
                List<ReportDataRow> tempList=metaDataRowsByLayOutKeyMap.get(metaDataRow.getLayOutRowKey());
                tempList.add(metaDataRow);
            }else{
                List<ReportDataRow> tempList=new ArrayList<>();
                tempList.add(metaDataRow);
                metaDataRowsByLayOutKeyMap.put(metaDataRow.getLayOutRowKey(),tempList);
            }
        }

        for (int i = 0; i < reportDataRows.size(); i++) {
            ReportDataRow reportDataRowOne = reportDataRows.get(i);
            //布局+维度列拼接的资源定位符
            String key = this.getDimensionKeyOfOneRow(reportDataRowOne, dimensionColumnList);
            ReportDataRow dataRow = new ReportDataRow();
            Map<String, Object> exprContext = new HashMap<>();
            //将所有的统计列保存到一个报表单元格与计算的map中
            for (ReportColumn statColumn : statColumns) {
                ReportDataCell cell = reportDataRowOne.getCell(statColumn.getSqlColumnName());
                //计算列的话这边value值为null,新建一个ReportDataCell对象
                if(cell==null){
                    cell=new ReportDataCell(statColumn,statColumn.getSqlColumnName(),"");
                }
                dataRow.add(cell);
                exprContext.put(statColumn.getSqlColumnName(), cell.getValue());
            }
            //遍历计算列进行数据计算
            for (ReportColumn column : computedColumns) {
                //参与占比的列名
                String colName=null;
                String eExpression="";
                ColumnExpressionConfig expression=column.getSetting().getExpression();
                if(expression==null){
                    continue;
                }
                //占比列
                if(ExpressionTypeEnum.PERCENT==expression.getType()){
                    colName=expression.getValue();
                    if(!StringUtils.isEmpty(colName)){
                        //按照布局列与参与的列进行占比统计
                        Object currentCellValue = reportDataRowOne.getCellValue(colName);
                        List<ReportDataRow> currentPercntRows=metaDataRowsByLayOutKeyMap.get(reportDataRowOne.getLayOutRowKey());
                        if(currentCellValue!=null&&currentPercntRows!=null&& CellFormatUtil.isNumber(currentCellValue.toString())){
                            List<String> perCellValueList=new ArrayList<>();
                            for(ReportDataRow reportDataRow :currentPercntRows){
                                Object perCellValue = reportDataRow.getCellValue(colName);
                                if(perCellValue!=null&& CellFormatUtil.isNumber(perCellValue.toString())){
                                    perCellValueList.add(perCellValue.toString());
                                }
                            }
                            eExpression=currentCellValue.toString()+"*1.0"+"/"+"("+StringUtils.join(perCellValueList,"+")+")";
                        }
                    }
                }else if(ExpressionTypeEnum.CUSTOMIZE== expression.getType()){
                    eExpression=column.getSetting().getExpression().getValue();
                }else{
                    continue;
                }
                Object value = AviatorExprUtil.execute(eExpression, exprContext);
                dataRow.getCell(column.getSqlColumnName()).setValue(value);
                exprContext.put(column.getSqlColumnName(), value);
            }
            dimensionKeyAndRowMap.put(key, dataRow);
        }
        return dimensionKeyAndRowMap;
    }

    protected String getDimensionKeyOfOneRow(ReportDataRow metaDataRow, List<ReportColumn> nonStatColumns) {
        StringBuilder rowMapKeyBuilder = new StringBuilder("");
        for (ReportColumn nonStatColumn : nonStatColumns) {
            String value = this.getMetaCellValue(metaDataRow, nonStatColumn);
            rowMapKeyBuilder.append(StringUtils.replace(value, PATH_SEPARATOR, "*"));
            rowMapKeyBuilder.append(PATH_SEPARATOR);
        }
        return rowMapKeyBuilder.toString();
    }

    public ColumnTree getTopDimensionColumnTree() {
        return topDimensionColumnTree;
    }

    public void setTopDimensionColumnTree(ColumnTree topDimensionColumnTree) {
        this.topDimensionColumnTree = topDimensionColumnTree;
    }

    public ColumnTree getStatisticalColumnTree() {
        return statisticalColumnTree;
    }

    public void setStatisticalColumnTree(ColumnTree statisticalColumnTree) {
        this.statisticalColumnTree = statisticalColumnTree;
    }

    public List<ReportDataRow> getReportDataRows() {
        return reportDataRows;
    }

    public void setReportDataRows(List<ReportDataRow> reportDataRows) {
        this.reportDataRows = reportDataRows;
    }

    public List<ReportColumn> getReportDataColumnList() {
        return reportDataColumnList;
    }

    public void setReportDataColumnList(List<ReportColumn> reportDataColumnList) {
        this.reportDataColumnList = reportDataColumnList;
    }

    public List<ReportColumn> getLeftDimensionList() {
        return leftDimensionList;
    }

    public void setLeftDimensionList(List<ReportColumn> leftDimensionList) {
        this.leftDimensionList = leftDimensionList;
    }

    public List<ReportColumn> getTopDimensionList() {
        return topDimensionList;
    }

    public void setTopDimensionList(List<ReportColumn> topDimensionList) {
        this.topDimensionList = topDimensionList;
    }

    public List<ReportColumn> getRealStatisticalList() {
        return realStatisticalList;
    }

    public void setRealStatisticalList(List<ReportColumn> realStatisticalList) {
        this.realStatisticalList = realStatisticalList;
    }

    public List<ReportColumn> getComputeStatisticalList() {
        return computeStatisticalList;
    }

    public void setComputeStatisticalList(List<ReportColumn> computeStatisticalList) {
        this.computeStatisticalList = computeStatisticalList;
    }

    public ColumnTree getTopColumnTree() {
        return topColumnTree;
    }

    public void setTopColumnTree(ColumnTree topColumnTree) {
        this.topColumnTree = topColumnTree;
    }

    public ColumnTree getLeftDimensionColumnTree() {
        return leftDimensionColumnTree;
    }

    public void setLeftDimensionColumnTree(ColumnTree leftDimensionColumnTree) {
        this.leftDimensionColumnTree = leftDimensionColumnTree;
    }

    public Long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(Long totalCount) {
        this.totalCount = totalCount;
    }

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
}
